<template>
  <div ref="chartRef" class="bar-chart-container" :style="{ width: (props.width) + 'px', height: ( props.height) + 'px', background: (props.bgColor) || 'none' }" >
    <div v-if="renderError" class="chart-error">渲染错误：{{ renderError }}</div>
    <div v-else-if="hasConfigError" class="chart-error">配置错误：{{ configErrorMsg }}</div>
    <div v-else>
      <div v-if="loading" class="chart-loading">
        <i class="el-icon-loading"></i>
        加载中...
      </div>
      <div v-else-if="isEmpty" class="chart-empty">
        <i class="el-icon-data-analysis"></i>
        暂无数据
      </div>
      <!-- ECharts 图表会渲染在 chartRef 容器内 -->
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, watch, onMounted, onBeforeUnmount, nextTick, computed, onErrorCaptured, markRaw } from 'vue'
import * as echarts from 'echarts'
import { ComponentInstance } from '@/types/visual-app'
import { log } from 'console';

const emit = defineEmits(['bar-click', 'error'])
const props = defineProps<ComponentInstance>()

const chartRef = ref<HTMLElement>()
// 假设 chartInstance 是 ECharts 实例
//const chartInstance = ref<ECharts>(null)
const chartInstance = ref<echarts.ECharts | null>(null);

const hasSetOption = ref(false)
const loading = ref(false)
const isEmpty = ref(false)

// 错误兜底
const renderError = ref('')
onErrorCaptured((err) => {
  renderError.value = err instanceof Error ? err.message : String(err)
  return false // 阻止冒泡
})

// 配置错误防御
const configErrorMsg = ref('')
const hasConfigError = computed(() => {
  try {
    const c = {
      title: localTitle.value,
      showTitle: localShowTitle.value,
      showLegend: localShowLegend.value,
      showValue: localShowValue.value,
      valueFormat: localValueFormat.value,
      precision: localPrecision.value,
      categories: localCategories.value,
      series: localSeries.value,
      colors: localColors.value,
      barWidth: localBarWidth.value,
      option: localCustomOption.value
    }
    if (!Array.isArray(c.series)) {
      configErrorMsg.value = 'series 必须为数组'
      return true
    }
    if (!Array.isArray(c.categories)) {
      configErrorMsg.value = 'categories 必须为数组'
      return true
    }
    for (const s of c.series) {
      if (!Array.isArray(s.data)) {
        configErrorMsg.value = 'series.data 必须为数组'
        return true
      }
      if (!s.data.every((n: any) => typeof n === 'number')) {
        configErrorMsg.value = 'series.data 必须全为数字'
        return true
      }
    }
    if (!Array.isArray(c.colors)) {
      configErrorMsg.value = 'colors 必须为数组'
      return true
    }
    if (typeof c.barWidth !== 'number' || isNaN(c.barWidth)) {
      configErrorMsg.value = 'barWidth 必须为数字'
      return true
    }
    if (typeof c.precision !== 'number' || isNaN(c.precision)) {
      configErrorMsg.value = 'precision 必须为数字'
      return true
    }
    if (!['number', 'percent', 'currency'].includes(c.valueFormat)) {
      configErrorMsg.value = 'valueFormat 必须为 number/percent/currency'
      return true
    }
    if (typeof c.option !== 'object' || c.option === null) {
      configErrorMsg.value = 'option 必须为对象'
      return true
    }
    return false
  } catch (e) {
    configErrorMsg.value = String(e)
    return true
  }
})

// 本地变量
const localTitle = ref<string>(typeof props.config.title === 'string' ? props.config.title : '');
const localShowTitle = ref<boolean>(typeof props.config.showTitle === 'boolean' ? props.config.showTitle : true);
const localShowLegend = ref<boolean>(typeof props.config.showLegend === 'boolean' ? props.config.showLegend : true);
const localShowValue = ref<boolean>(typeof props.config.showValue === 'boolean' ? props.config.showValue : false);
const localValueFormat = ref<'number' | 'percent' | 'currency'>(['number', 'percent', 'currency'].includes(props.config.valueFormat) ? props.config.valueFormat : 'number');
const localPrecision = ref<number>(typeof props.config.precision === 'number' && !isNaN(props.config.precision) ? props.config.precision : 2);
const localCategories = ref<string[]>(Array.isArray(props.config.categories) ? props.config.categories.filter(v => typeof v === 'string') : typeof props.config.categories === 'string' ? props.config.categories.split(',').map(s => s.trim()).filter(s => s) : []);
const localSeries = ref<any[]>(Array.isArray(props.config.series) ? props.config.series : typeof props.config.series === 'string' ? (() => { try { const arr = JSON.parse(props.config.series); return Array.isArray(arr) ? arr : []; } catch { return []; } })() : []);
const localThemeColor = ref<string>(typeof props.config.themeColor === 'string' ? props.config.themeColor : '#409eff');
const localBarWidth = ref<number>(typeof props.config.barWidth === 'number' && !isNaN(props.config.barWidth) ? props.config.barWidth : typeof props.config.barWidth === 'string' && !isNaN(parseFloat(props.config.barWidth)) ? parseFloat(props.config.barWidth) : 50);
const localCustomOption = ref<object>(typeof props.config.customOption === 'object' && props.config.customOption !== null ? props.config.customOption : typeof props.config.customOption === 'string' ? (() => { try { const obj = JSON.parse(props.config.customOption); return typeof obj === 'object' && obj !== null ? obj : {}; } catch { return {}; } })() : {});
const localColors = ref<string[]>(Array.isArray(props.config.colors) ? props.config.colors.filter(v => typeof v === 'string') : typeof props.config.colors === 'string' ? props.config.colors.split(',').map(s => s.trim()).filter(s => s) : []);

watch(() => props.config.title, val => {
  localTitle.value = typeof val === 'string' ? val : '';
});
watch(() => props.config.showTitle, val => {
  localShowTitle.value = typeof val === 'boolean' ? val : true;
});
watch(() => props.config.showLegend, val => {
  localShowLegend.value = typeof val === 'boolean' ? val : true;
});
watch(() => props.config.showValue, val => {
  localShowValue.value = typeof val === 'boolean' ? val : false;
});
watch(() => props.config.valueFormat, val => {
  localValueFormat.value = ['number', 'percent', 'currency'].includes(val) ? val : 'number';
});
watch(() => props.config.precision, val => {
  localPrecision.value = typeof val === 'number' && !isNaN(val) ? val : 2;
});
watch(() => props.config.series, val => {
  let arr: any[] = [];
  if (Array.isArray(val)) {
    arr = val;
  } else if (typeof val === 'string') {
    try {
      const parsed = JSON.parse(val);
      if (Array.isArray(parsed)) arr = parsed;
    } catch {}
  }
  localSeries.value = arr;
},
{ deep: true });

watch(() => props.config.categories, val => {
  let arr: string[] = [];
  if (Array.isArray(val)) {
    arr = val.filter(v => typeof v === 'string');
  } else if (typeof val === 'string') {
    arr = val.split(',').map(s => s.trim()).filter(s => s);
  }
  localCategories.value = arr;
},
{ deep: true });

watch(() => props.config.customOption, val => {
  let obj: object = {};
  if (typeof val === 'object' && val !== null) {
    obj = val;
  } else if (typeof val === 'string') {
    try {
      const parsed = JSON.parse(val);
      if (typeof parsed === 'object' && parsed !== null) obj = parsed;
    } catch {}
  }
  localCustomOption.value = obj;
},
{ deep: true });

watch(() => props.config.colors, val => {
  let arr: string[] = [];
  if (Array.isArray(val)) {
    arr = val.filter(v => typeof v === 'string');
  } else if (typeof val === 'string') {
    arr = val.split(',').map(s => s.trim()).filter(s => s);
  }
  localColors.value = arr;
},
{ deep: true });

watch(() => props.config.barWidth, val => {
  let num = 50;
  if (typeof val === 'number' && !isNaN(val)) {
    num = val;
  } else if (typeof val === 'string') {
    const parsed = parseFloat(val);
    if (!isNaN(parsed)) num = parsed;
  }
  localBarWidth.value = num;
});

watch(() => props.config.themeColor, val => {
  if (typeof val === 'string') {
    localThemeColor.value = val;
  } else if (Array.isArray(val) && val.length > 0 && typeof val[0] === 'string') {
    localThemeColor.value = val[0];
  } else {
    localThemeColor.value = '#409eff';
  }
});

// 只在 width/height 变化时 resize，不重复 renderChart
// watch(() => props.width, () => {
//   try {
//     handleResize();
//   } catch (e) {
//     console.error('BarChart width watcher error:', e);
//   }
// });

// watch(() => props.height, () => {
//   try {
//     handleResize();
//   } catch (e) {
//     console.error('BarChart height watcher error:', e);
//   }
// });

// 窗口自适应
function handleResize() {
  if (!chartInstance.value || chartInstance.value.isDisposed()) {
    return;
  }
  try {
    chartInstance.value.resize();
  } catch (error) {
    console.error('图表 resize 失败:', error);
  }
}


watch(
  [
    localTitle,
    localShowTitle,
    localShowLegend,
    localShowValue,
    localValueFormat,
    localPrecision,
    localCategories,
    localSeries,
    localThemeColor,
    localBarWidth,
    localCustomOption,
    localColors
  ],
  () => {
    renderChart();
  },
  { deep: true }
);

function getOption() {
  return {
    title: localShowTitle.value === false ? undefined : {
      text: localTitle.value || '',
      left: 'center',
      textStyle: {
        color: localThemeColor.value
      }
    },
    type:'bar',
    tooltip: { trigger: 'axis' },
    legend: {
      show: localShowLegend.value,
      textStyle: { color: localThemeColor.value }
    },
    grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
    xAxis: {
      type: 'category',
      data: localCategories.value,
      axisLine: { lineStyle: { color: localThemeColor.value } },
      axisLabel: { color: localThemeColor.value }
    },
    yAxis: {
      type: 'value',
      axisLine: { lineStyle: { color: localThemeColor.value } },
      axisLabel: { color: localThemeColor.value }
    },
    color: localColors.value.length > 0 ? localColors.value : [localThemeColor.value],
    series: (Array.isArray(localSeries.value) ? localSeries.value : []).map((s: any, i: number) => ({
      name: s.name,
      type: 'bar',
      data: Array.isArray(s.data) ? s.data : [],
      itemStyle: {
        color: s.color || localColors.value[i % localColors.value.length] || localThemeColor.value,
        borderRadius: [4, 4, 0, 0]
      },
      barWidth: localBarWidth.value + '%',
      label: {
        show: localShowValue.value,
        position: 'top',
        formatter: (params: any) => {
          switch (localValueFormat.value) {
            case 'percent': return (params.value * 100).toFixed(localPrecision.value) + '%'
            case 'currency': return '¥' + params.value.toFixed(localPrecision.value)
            default: return params.value.toFixed(localPrecision.value)
          }
        },
        color: '#666',
        fontSize: 12
      }
    })),
    ...(typeof localCustomOption.value === 'object' && localCustomOption.value !== null ? localCustomOption.value : {})
  }
}

function isValidConfig(config: any) {
  return (
    Array.isArray(config.categories) &&
    config.categories.every((x: any) => typeof x === 'string') &&
    Array.isArray(config.series) &&
    config.series.every((s: any) =>
      typeof s === 'object' &&
      typeof s.name === 'string' &&
      Array.isArray(s.data) &&
      s.data.every((n: any) => typeof n === 'number')
    ) &&
    Array.isArray(config.colors) &&
    config.colors.every((x: any) => typeof x === 'string') &&
    typeof config.barWidth === 'number' &&
    !isNaN(config.barWidth) &&
    typeof config.precision === 'number' &&
    !isNaN(config.precision) &&
    ['number', 'percent', 'currency'].includes(config.valueFormat) &&
    typeof config.option === 'object' &&
    config.option !== null
  )
}

function renderChart() {
  if (!chartRef.value || !chartRef.value.isConnected) return
  const localConfig = {
    title: localTitle.value,
    showTitle: localShowTitle.value,
    showLegend: localShowLegend.value,
    showValue: localShowValue.value,
    valueFormat: localValueFormat.value,
    precision: localPrecision.value,
    categories: localCategories.value,
    series: localSeries.value,
    colors: localColors.value,
    barWidth: localBarWidth.value,
    option: localCustomOption.value
  }
  if (!isValidConfig(localConfig)) {
    renderError.value = '参数类型不符，渲染已阻止。请检查 OptionPanel 配置！'
    console.error('BarChart render 参数类型不符:', localConfig)
    return
  }
  if (!chartInstance.value) {
    chartInstance.value = markRaw(echarts.init(chartRef.value));
  }
  // 再次 DOM 检查
  if (!chartRef.value || !chartRef.value.isConnected) return
  try {
    const option = getOption()
    chartInstance.value.setOption(option, true);
    hasSetOption.value = true;
    isEmpty.value = !localCategories.value.length || !localSeries.value.length
  } catch (e) {
    renderError.value = 'ECharts 渲染异常: ' + (e instanceof Error ? e.message : String(e))
  }
}

let resizeObserver: ResizeObserver | null = null;

onMounted(() => {
  nextTick(() => {
    renderChart();
    if (chartRef.value) {
      resizeObserver = new ResizeObserver(() => {
        handleResize();
      });
      resizeObserver.observe(chartRef.value);
    }
  });
});

onBeforeUnmount(() => {
  if (resizeObserver && chartRef.value) {
    resizeObserver.unobserve(chartRef.value);
    resizeObserver.disconnect();
    resizeObserver = null;
  }
  setTimeout(() => {
    if (chartInstance.value) {
      try {
        if (chartRef.value && chartRef.value.isConnected) {
          chartInstance.value.dispose();
        }
      } catch (e) {}
      chartInstance.value = null;
    }
  }, 0);
});
</script>

<style scoped>
.bar-chart-container {
  width: 100%;
  height: 100%;
  min-width: 100px;
  min-height: 80px;
  position: relative;
}

.chart-loading, .chart-empty {
  position: absolute;
  left: 0; 
  top: 0; 
  right: 0; 
  bottom: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  color: #999;
  font-size: 14px;
  background: rgba(255,255,255,0.8);
  z-index: 2;
}

.chart-loading i, .chart-empty i {
  font-size: 24px;
  margin-bottom: 8px;
  opacity: 0.6;
}
.chart-error {
  color: #f56c6c;
  background: #fff0f0;
  border: 1px solid #fbc4c4;
  border-radius: 4px;
  padding: 16px;
  margin: 16px;
  font-size: 14px;
  text-align: center;
}
</style>